package aliyun_sms

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"encoding/json"
	"errors"

	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"strings"
	"time"
	"util"
)

type SmsClient struct {
	AccessKeyId     string
	AccessKeySecret string
}

var format = "JSON"
var version = "2016-09-27"
var signatureMethod = "HMAC-SHA1"
var signatureVersion = "1.0"
var timeFormatTmp = "2006-01-02T15:05:06Z"
var action = "SingleSendSms"
var httpMethod = "POST"

var remoteUrl = "https://sms.aliyuncs.com/"

func New(accessKeyId, accessKeySecret string) *SmsClient {
	return &SmsClient{AccessKeyId: accessKeyId,
		AccessKeySecret: accessKeySecret}
}

func (s *SmsClient) SendSms(mobile []string, signName,
	templateCode string, param map[string]string) (bool, error) {
	if len(s.AccessKeyId) == 0 || len(s.AccessKeySecret) == 0 {
		return false, errors.New("client is not init")
	}
	if len(mobile) == 0 {
		return false, errors.New("mobile is empty")
	}
	value := make(url.Values)
	value.Add("Action", action)
	value.Add("SignName", signName)
	value.Add("TemplateCode", templateCode)
	value.Add("RecNum", strings.Join(mobile, ","))
	if len(param) > 0 {
		if paramstring, err := json.Marshal(param); err != nil {
			return false, err
		} else {
			value.Add("ParamString", string(paramstring))
		}
	}
	value.Add("Format", format)
	value.Add("Version", version)
	value.Add("AccessKeyId", s.AccessKeyId)
	value.Add("SignatureMethod", signatureMethod)
	value.Add("SignatureVersion", signatureVersion)
	value.Add("Timestamp", GetISO8601TimeStamp(time.Now()))
	value.Add("SignatureNonce", util.RandomAlphanumeric(32))

	log.Println(CreateSignatureForRequest("POST", &value, s.AccessKeySecret))

	value.Add("Signature", CreateSignatureForRequest("POST", &value, s.AccessKeySecret))

	if resp, err := http.PostForm(remoteUrl, value); err == nil {
		defer resp.Body.Close()
		if resp.StatusCode == 200 {
			return true, nil
		} else {
			bytes, _ := ioutil.ReadAll(resp.Body)
			return false, errors.New(string(bytes))
		}
	} else {
		return false, err
	}

}

//CreateSignature creates signature for string following Aliyun rules
func CreateSignature(stringToSignature, accessKeySecret string) string {
	// Crypto by HMAC-SHA1
	hmacSha1 := hmac.New(sha1.New, []byte(accessKeySecret))
	hmacSha1.Write([]byte(stringToSignature))
	sign := hmacSha1.Sum(nil)

	// Encode to Base64
	base64Sign := base64.StdEncoding.EncodeToString(sign)

	return base64Sign
}

func percentReplace(str string) string {
	str = strings.Replace(str, "+", "%20", -1)
	str = strings.Replace(str, "*", "%2A", -1)
	str = strings.Replace(str, "%7E", "~", -1)

	return str
}

// CreateSignatureForRequest creates signature for query string values
func CreateSignatureForRequest(method string, values *url.Values, accessKeySecret string) string {
	log.Println(values.Encode())

	canonicalizedQueryString := percentReplace(values.Encode())

	stringToSign := method + "&%2F&" + url.QueryEscape(canonicalizedQueryString)

	return CreateSignature(stringToSign, accessKeySecret+"&")
}
